﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NMF.Optimizations.Linq
{
    /// <summary>
    /// Represents the default implementation for the Where operation
    /// </summary>
    /// <typeparam name="T">The inner type of the filtered monad instance</typeparam>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    public class FilteredSource<T> : ObservableSourceBase<T>, IObservableSource<T>, ISourceObserver<T>
    {
        private HashSet<T> whiteList = new HashSet<T>();

        internal FilteredSource(IObservableSource<T> source, Func<T, bool> filter)
        {
            this.Source = source;
            this.Filter = filter;

            foreach (var item in source)
            {
                if (Filter(item)) whiteList.Add(item);
            }

            source.Subscribe(this);
        }

        void ISourceObserver<T>.OnNewItem(T obj)
        {
            if (Filter(obj))
            {
                whiteList.Add(obj);
                OnNewItem(obj);
            }
        }

        public void Refresh(T item)
        {
            if (!whiteList.Contains(item))
            {
                if (Filter(item))
                {
                    whiteList.Add(item);
                    OnNewItem(item);
                }
            }
            else
            {
                if (Filter(item))
                {
                    OnUpdatedItem(item);
                }
                else
                {
                    whiteList.Remove(item);
                    OnDeletedItem(item);
                }
            }
        }

        public void RefreshAll()
        {
            foreach (var item in Source)
            {
                Refresh(item);
            }
        }

        void ISourceObserver<T>.OnUpdatedItem(T obj)
        {
            Refresh(obj);
        }

        void ISourceObserver<T>.OnDeletedItem(T obj)
        {
            if (whiteList.Contains(obj))
            {
                whiteList.Remove(obj);
                OnDeletedItem(obj);
            }
        }

        void ISourceObserver<T>.OnReset()
        {
            whiteList.Clear();
            OnReset();
        }

        public IObservableSource<T> Source { get; private set; }
        public Func<T, bool> Filter { get; private set; }

        public override IEnumerator<T> GetEnumerator()
        {
            return whiteList.GetEnumerator();
        }
    }
}
